/*
  amingo - 2D Game Engine
  Copyright (C) 2009 Bradley Zeis

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

  Bradley Zeis
  amingoengine@gmail.com
*/

#include <Python.h>
#include "structmember.h"

#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif

/* Structure includes */
#ifndef FLTIME
#define FLTIME 0
#endif

#ifndef FLOPS
#define FLOPS 0
#endif

#ifndef FLMATH
#define FLMATH 0
#endif

#ifndef DISPLAY
#define DISPLAY 0
#endif

#ifndef FLCOLOR
#define FLCOLOR 0
#endif

#ifndef IMAGE
#define IMAGE 0
#endif

#ifndef MESH
#define MESH 0
#endif

#ifndef SPRITE
#define SPRITE 0
#endif

#ifndef SCREEN
#define SCREEN 0
#endif

/* -------- MACROS -------- */
#if FLOPS
#define ADD 0
#define SUB 1
#define MUL 2
#define DIV 3
#define FLR 4
#define MOD 5
#define POW 6
#endif

#define PI (3.1415926535897931)
#define DEGTORAD(d) ((PI * (d)) / 180.0)
#define RADTODEG(r) ((180 * (r)) / PI)

#define GETITEM(s, i) (PySequence_GetItem((s), (i)))

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

#ifndef CLOSURE
#define CLOSURE (void *)NULL
#endif

/* ------- CONSTANTS -------- */
#define SCREEN_BLOCK_DRAW       1<<0
#define SCREEN_CULL_SPRITES     1<<1
#define SCREEN_DRAW_TEXTURES    1<<2
#define SCREEN_DRAW_MESHES      1<<3
#define SCREEN_DRAW_WIREFRAME   1<<4

#define POLYGON_STATIC          1<<0
#define POLYGON_AXIS_ALIGNED    1<<1
#define POLYGON_MUTABLE_LENGTH  1<<2

#define TIME_PAUSED             1<<0
#define TIMER_ACTIVE            1<<1

#define BONE_CONSTRAIN_LENGTH   1<<0
#define BONE_CONSTRAIN_ANGLE    1<<1
#define BONE_CONSTRAIN          (BONE_CONSTRAIN_LENGTH | BONE_CONSTRAIN_ANGLE)

/* -------- TYPE STRUCTS ------- */
/* fltime */
#if FLTIME != 0
typedef struct {
    PyObject_HEAD
    long int offset;
    long int current;
    long int last;
    double fps;
    unsigned char flags;
} Clock;

typedef struct {
    PyObject_HEAD
    long int start;
    long int length;
    long int current;
    unsigned char flags;
} Timer;
#endif

/* flmath */
#if FLMATH != 0
typedef struct {
    double x;
    double y;
} Point;

typedef struct {
    PyObject_HEAD
    Point *pt;
} Vector;

typedef struct {
    PyObject_HEAD
    Vector *center;
    int length;
    Vector **points;
    Vector **edges;
    double *orients;
    double orientation;
    unsigned char flags;
} Polygon;

typedef struct {
    PyObject_HEAD
    Vector *center;
    int length;
    Vector **points;
    Vector **edges;
    double *orients;
    double orientation;
    unsigned char flags;
    double width;
    double height;
} Rect;

typedef struct {
    double min;
    double max;
} Projection;
#endif

/* display */
#if DISPLAY != 0
typedef struct {
    void *screen;
    int width;
    int height;
    int depth;
    int flags;
} Display;
#endif

/* flcolor */
#if FLCOLOR != 0
typedef struct {
    PyObject_HEAD
    double r;
    double g;
    double b;
    double a;
} Color;
#endif

/* image */
#if IMAGE != 0
typedef struct {
    PyObject_HEAD
    int width;
    int height;
    int speed;

    int length;
    unsigned int *textures;
    int frame;

    int last_frame_switch;
} Texture;
#endif

/* mesh */
#if MESH != 0
typedef struct _Bone {
    PyObject_HEAD
    struct _Bone *parent;
    int child_count;
    int allocated;
    struct _Bone **children;
    
    Vector *v;
    
    double min_length;
    double max_length;
    double min_angle;
    double max_angle;
    
    unsigned char flags;
} Bone;

typedef struct {
    PyObject_HEAD
    Point *pt;
} MeshNode;

typedef struct {
    PyObject_HEAD
    Point *pt;
    
    int bone_count;
    Bone **bones;
    double *weights;
} VMeshNode;

typedef struct {
    PyObject_HEAD
    int length;
    Point *vcoords;
    Point *tcoords;
    Vector **vnodes;
    Vector **tnodes;
    PyObject *vlist;
    PyObject *tlist;
} Mesh;
#endif

/* sprite */
#if SPRITE != 0
typedef struct {
    PyObject_HEAD
    Polygon *bound;
    Color *color;
} ObjectState;

typedef struct {
    PyObject_HEAD
    PyObject *groups;

    PyObject *parent;
    PyObject *children;
    int tier;

    ObjectState *state;
    ObjectState *interstate;
    ObjectState *prevstate;
    int z;

    Texture *texture;
    PyObject *skins;
    Bone *bones;
    Mesh *mesh;

    unsigned char flags;
} Sprite;

typedef struct {
    PyObject_HEAD
    PyObject *sprites;
} Group;

typedef struct {
    PyObject_HEAD
    PyObject *sprites;
    PyObject *sorted;
    PyObject *active;
} MGroup;
#endif

/* screen */
#if SCREEN != 0
typedef struct {
    PyObject_HEAD
} Painter;

typedef struct {
    PyObject_HEAD
    Vector *center;
    int length;
    Vector **points;
    Vector **edges;
    double *orients;
    double orientation;
    unsigned char flags;
    double width;
    double height;

    Rect *local_rect;
    Rect *bound_rect;
    double zoom;
} Screen;
#endif

/* -------- UTILITY -------- */
PyObject *ImportModule(char *path) {
    PyObject *sysmodules = PyImport_GetModuleDict();
    PyObject *module;
    if (PyMapping_HasKeyString(sysmodules, path)) {
        module = PyMapping_GetItemString(sysmodules, path);
    } else {
        module = PyImport_ImportModule(path);
        if (!module) {
          PyErr_Format(PyExc_ImportError, "couldn't import module '%s'", path);
          return NULL;
        }
    }
    return module;
}
